package com.sh.data.engine.domain.normalization.modeldesign.model.service.impl;

import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.CreateTableDomain;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.ModelDomain;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.ModelDomain.Field;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.param.SaveModelParam;
import com.sh.data.engine.domain.normalization.modeldesign.model.service.TableOperationStrategy;
import com.sh.data.engine.domain.normalization.modeldesign.model.service.impl.ModelServiceImpl.FieldsParseResult;
import com.sh.data.engine.domain.shims.db.BaseDbManager;
import com.sh.data.engine.domain.shims.db.model.FieldInfoDomain;
import com.sh.data.engine.domain.shims.db.model.TableInfoDomain;
import com.sh.data.engine.domain.shims.sql.builder.DDLBuilder;
import com.sh.data.engine.domain.shims.sql.ddl.AddTableFieldParam;
import com.sh.data.engine.domain.shims.sql.ddl.CreateTableParam;
import com.sh.data.engine.domain.shims.sql.ddl.EditTableFieldParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
public class DMTableOperation implements TableOperationStrategy {

    @Override
    public List<Field> getTableField(String dbName, String schema, String tableName,
                                     BaseDbManager dbManager) {
        List<ModelDomain.Field> resultFields = new ArrayList<>();
        try {
            // connection已经在方法内close了
            List<FieldInfoDomain> fieldList = dbManager.getFieldList(schema, tableName);
            for (FieldInfoDomain dbField : fieldList) {
                ModelDomain.Field resultField = new ModelDomain.Field();
                resultField.setFieldName(dbField.getFieldName());
                resultField.setFieldDesc(dbField.getComment());
                resultField.setFieldType(dbField.getFieldType());
                resultField.setRequired(!dbField.isNullable());
                resultFields.add(resultField);
            }
        } catch (SQLException e) {
            log.error("DM下: 数据库:{} 获取表字段:{}, 异常:{}", dbName, tableName, e.getMessage());
            throw new BusinessException(
                "DM下: 数据库:" + dbName + " 获取表字段:" + tableName + " 异常:" + e.getMessage());
        }
        return resultFields;
    }

    @Override
    public boolean createTable(CreateTableDomain createTableDomain) {

        BaseDbManager dbManager = createTableDomain.getDbManager();
        CreateTableParam createTableParam = new CreateTableParam();
        createTableParam.setSchema(createTableDomain.getSchemaName());
        createTableParam.setTableName(createTableDomain.getTableName());
        createTableParam.setTableComment(createTableDomain.getTableComment());
        List<CreateTableParam.FieldInfo> fieldInfoList = new ArrayList<>();
        for (SaveModelParam.Field field : createTableDomain.getFieldList()) {
            CreateTableParam.FieldInfo fieldInfo = new CreateTableParam.FieldInfo();
            fieldInfo.setFieldName(field.getFieldName());
            fieldInfo.setComment(field.getFieldDesc());
            // 设置为hive支持的字段类型
            fieldInfo.setFieldType(field.getFieldType());
            fieldInfo.setNullable(!field.getRequired());
            // 暂不需要给id设置主键和自增
            //        if (StringUtils.equals(field.getFieldName(), "id")) {
            //          fieldInfo.setAutoIncrement(true);
            //          fieldInfo.setPrimaryKey(true);
            //        }
            fieldInfoList.add(fieldInfo);
        }
        createTableParam.setFieldInfoList(fieldInfoList);
        createTableParam.setDbName(createTableDomain.getDbName());
        DDLBuilder dmDDLBuilder = null;
        try {
            dmDDLBuilder = dbManager.getDDLBuilder();
            dmDDLBuilder.create(createTableParam).execute();
            return true;
        } catch (SQLException e) {
            log.error(
                "pg 数据库:{} 创建表:{} 异常:{}",
                createTableDomain.getSchemaName(),
                createTableDomain.getTableName(),
                e.getMessage());
            return false;
        } finally {
            // 关闭
            if (dbManager != null) {
                dbManager.close(dmDDLBuilder);
            }
        }
    }

    @Override
    public boolean editFormTable(String dbName, String schema, ModelDomain dbModel,
                                 SaveModelParam saveModelParam, BaseDbManager dbManager) {

        ModelServiceImpl.FieldsParseResult fieldsParseResult =
            this.parseFields(dbModel, saveModelParam);
        List<SaveModelParam.Field> updateFields = fieldsParseResult.getUpdateFieldList();
        List<SaveModelParam.Field> newFields = fieldsParseResult.getNewFieldList();
        List<SaveModelParam.Field> deleteFields = fieldsParseResult.getDeleteFieldList();
        String newTableName = saveModelParam.getEnName();
        String newTableComment = saveModelParam.getCnName();
        DDLBuilder pgDDLBuilder = null;
        try {
            pgDDLBuilder = dbManager.getDDLBuilder();

            // 2.表注释修改
            if (!StringUtils.equals(dbModel.getCnName(), newTableComment)) {
                try {
                    pgDDLBuilder
                        .reset()
                        .alterTableComment(dbName, dbModel.getEnName(), newTableComment)
                        .execute();
                } catch (SQLException e) { // 编辑视图注释
                    pgDDLBuilder
                        .reset()
                        .alterViewComment(dbName, dbModel.getEnName(), newTableComment)
                        .execute();
                }
            }

            // 添加字段
            if (CollectionUtils.isNotEmpty(newFields)) {
                List<AddTableFieldParam> paramList = new ArrayList<>();
                for (SaveModelParam.Field newField : newFields) {
                    AddTableFieldParam param = new AddTableFieldParam();
                    param.setFieldName(newField.getFieldName());
                    param.setFieldType(newField.getFieldType());
                    param.setComment(newField.getFieldDesc());
                    param.setNullable(!newField.getRequired());
                    paramList.add(param);
                }
                pgDDLBuilder.reset().addTableField(schema, newTableName, paramList).execute();
            }

            // 删除字段
            if (CollectionUtils.isNotEmpty(deleteFields)) {
                final List<String> deletedFields =
                    deleteFields.stream()
                        .map(SaveModelParam.Field::getFieldName)
                        .collect(Collectors.toList());
                pgDDLBuilder.reset().deleteTableField(dbName, newTableName, deletedFields).execute();
            }

            // 修改字段
            if (CollectionUtils.isNotEmpty(updateFields)) {
                List<EditTableFieldParam> paramList = new ArrayList<>();
                for (SaveModelParam.Field updateField : updateFields) {
                    EditTableFieldParam param = new EditTableFieldParam();
                    param.setFieldName(updateField.getFieldName());
                    param.setNewFieldName(updateField.getFieldName());
                    param.setComment(updateField.getFieldDesc());
                    param.setNullable(!updateField.getRequired());
                    param.setFieldType(updateField.getFieldType());
                    paramList.add(param);
                }
                pgDDLBuilder.reset().editTableField(dbName, newTableName, paramList).execute();
            }

            // 1.表名修改
            if (!StringUtils.equals(dbModel.getEnName(), newTableName)) {
                List<String> tableNameList =
                    dbManager.getTableList(dbName, schema).stream()
                        .map(TableInfoDomain::getTableName)
                        .collect(Collectors.toList());
                if (tableNameList.contains(newTableName)) {
                    throw new BusinessException("存在相同模型表名");
                }
                pgDDLBuilder.reset().alterTableName(schema, dbModel.getEnName(), newTableName).execute();
            }
            return true;
        } catch (SQLException e) {
            log.error("PG模式下: pg 数据库:{} 编辑表:{} 异常:{}", schema, newTableName, e.getMessage());
            throw new BusinessException("编辑表失败 " + e.getMessage());
            // return false;
        } finally {
            // 关闭
            if (dbManager != null) {
                dbManager.close(pgDDLBuilder);
            }
        }
    }

    @Override
    public boolean isTableNameExist(String dbName, String schema, String tableName,
                                    BaseDbManager dbManager) {
        try {
            boolean pgTableNameExist = dbManager.isTableNameExist(schema, tableName);
            if (pgTableNameExist) {
                log.error("Pg模式下检查 数据源 {} 表{}已存在", schema, tableName);
                return true;
            }
        } catch (SQLException e) {
            log.error("Pg模式下检查数据源 {} 表{}是否存在时,发生异常: {}", schema, tableName, e.getMessage());
            return false;
        }
        return false;
    }

    @Override
    public TableInfoDomain getTableInfoDomain(String dbName, String schema, String tableName,
                                              BaseDbManager dbManager) {
        try {
            return dbManager.getTableInfoByTableName(dbName, tableName);
        } catch (SQLException e) {
            log.error(e.getMessage());
        }
        return null;
    }

    @Override
    public FieldsParseResult parseFields(ModelDomain dbModel, SaveModelParam saveModelParam) {
        List<ModelDomain.Field> dbFields = dbModel.getFields();
        Map<String, Field> dbFieldsMap =
            dbFields.stream()
                .collect(Collectors.toMap(ModelDomain.Field::getFieldName, Function.identity()));
        List<SaveModelParam.Field> paramFields = saveModelParam.getFields();
        Map<String, SaveModelParam.Field> paramFieldsMap =
            paramFields.stream()
                .collect(Collectors.toMap(SaveModelParam.Field::getFieldName, Function.identity()));
        // 新增的字段
        List<SaveModelParam.Field> newFieldList = new ArrayList();
        // 更新的字段
        List<SaveModelParam.Field> updateFieldList = new ArrayList();
        // 删除的字段
        List<SaveModelParam.Field> deleteFieldList = new ArrayList();
        for (SaveModelParam.Field paramField : paramFields) {
            if (!dbFieldsMap.containsKey(paramField.getFieldName())) {
                newFieldList.add(paramField);
            }
        }
        for (ModelDomain.Field dbField : dbFields) {
            if (paramFieldsMap.containsKey(dbField.getFieldName())) {
                SaveModelParam.Field paramField = paramFieldsMap.get(dbField.getFieldName());
                // 描述,类型是否改变, 判断类型时需要注意hive,pg字段类型不同
                boolean sameDescAndType =
                    Objects.equals(dbField.getFieldDesc(), paramField.getFieldDesc())
                        && (Objects.equals(dbField.getFieldType(), paramField.getFieldType()));
                // hive 检查是否分区, pg检查是否必填
                boolean sameField = Objects.equals(dbField.getRequired(), paramField.getRequired());

                if (!sameDescAndType || !sameField) {
                    SaveModelParam.Field updateField = new SaveModelParam.Field();
                    updateField.setFieldName(paramField.getFieldName());
                    updateField.setFieldDesc(paramField.getFieldDesc());
                    updateField.setFieldType(paramField.getFieldType());
                    updateField.setRequired(paramField.getRequired());
                    updateField.setIsPartition(paramField.getIsPartition());
                    updateFieldList.add(updateField);
                }
            } else {
                SaveModelParam.Field deleteItem = new SaveModelParam.Field();
                deleteItem.setFieldName(dbField.getFieldName());
                deleteFieldList.add(deleteItem);
            }
        }
        return ModelServiceImpl.FieldsParseResult.builder()
            .newFieldList(newFieldList)
            .updateFieldList(updateFieldList)
            .deleteFieldList(deleteFieldList)
            .build();
    }
}
